"""
音画同步服务实现
"""
import os
import json
import re
from typing import Dict, Any, List, Optional, Tuple, Callable
import threading
import time

from app.services.sync_service import ISyncService
from app.utils.logger import Logger
from app.services.service_locator import ServiceLocator
from app.services.audio_service import IAudioService
from app.services.speech_service import ISpeechService


class SyncService(ISyncService):
    """
    音画同步服务实现类
    """
    
    def __init__(self):
        """
        初始化音画同步服务
        """
        super().__init__()
        
        # 获取日志记录器
        self.logger = Logger.get_logger("sync_service")
        
        # 音画同步项目列表，每个项目为 (话术, 视频, 关键词) 的元组
        self._sync_items: List[Tuple[str, str, str]] = []
        
        # 音画同步是否启用
        self._enabled: bool = False
        
        # 默认配置
        self._config: Dict[str, Any] = {
            "default_save_path": "",  # 不再使用固定路径，而是依赖配置路径
            "keyword_match_mode": "contains",  # 可选值: contains, regex, exact
            "case_sensitive": False,
            "auto_save": True
        }
        
        # 线程锁，用于保护 _sync_items 和 _enabled
        self._lock = threading.RLock()
    
    def add_sync_item(self, speech: str, video: str, keyword: str) -> None:
        """
        添加音画同步项目
        
        @param speech: 话术
        @param video: 视频
        @param keyword: 关键词
        """
        with self._lock:
            self._sync_items.append((speech, video, keyword))
            self.logger.info(f"添加音画同步项目: 话术={speech}, 视频={video}, 关键词={keyword}")
            
            # 如果启用了自动保存，则保存到文件
            if self._config.get("auto_save", True):
                self.save_sync_items()
                
            # 触发项目添加事件
            self.notify("sync_item_added", {
                "speech": speech,
                "video": video,
                "keyword": keyword
            })
    
    def remove_sync_item(self, index: int) -> bool:
        """
        移除音画同步项目
        
        @param index: 项目索引
        @return: 是否成功移除
        """
        with self._lock:
            if 0 <= index < len(self._sync_items):
                item = self._sync_items.pop(index)
                self.logger.info(f"移除音画同步项目: 索引={index}, 项目={item}")
                
                # 如果启用了自动保存，则保存到文件
                if self._config.get("auto_save", True):
                    self.save_sync_items()
                
                # 触发项目移除事件
                self.notify("sync_item_removed", {
                    "index": index,
                    "item": item
                })
                
                return True
            else:
                self.logger.warning(f"移除音画同步项目失败: 索引 {index} 超出范围")
                return False
    
    def get_sync_items(self) -> List[Tuple[str, str, str]]:
        """
        获取所有音画同步项目
        
        @return: 音画同步项目列表，每个项目为 (话术, 视频, 关键词) 的元组
        """
        with self._lock:
            return self._sync_items.copy()
    
    def clear_sync_items(self) -> None:
        """
        清空所有音画同步项目
        """
        with self._lock:
            self._sync_items.clear()
            self.logger.info("清空所有音画同步项目")
            
            # 如果启用了自动保存，则保存到文件
            if self._config.get("auto_save", True):
                self.save_sync_items()
            
            # 触发项目清空事件
            self.notify("sync_items_cleared", {})
    
    def enable_sync(self, enabled: bool) -> None:
        """
        启用或禁用音画同步
        
        @param enabled: 是否启用
        """
        with self._lock:
            if self._enabled != enabled:
                self._enabled = enabled
                status = "已开启" if enabled else "已关闭"
                self.logger.info(f"音画同步{status}")
                
                # 触发状态变化事件
                self.notify("sync_status_changed", {
                    "enabled": enabled,
                    "status": status
                })
    
    def is_sync_enabled(self) -> bool:
        """
        检查音画同步是否启用
        
        @return: 是否启用
        """
        with self._lock:
            return self._enabled
    
    def match_keyword(self, text: str) -> Optional[Tuple[str, str, str]]:
        """
        匹配关键词
        
        @param text: 要匹配的文本
        @return: 匹配到的项目 (话术, 视频, 关键词)，如果没有匹配到则返回 None
        """
        if not self._enabled or not text:
            return None
        
        with self._lock:
            match_mode = self._config.get("keyword_match_mode", "contains")
            case_sensitive = self._config.get("case_sensitive", False)
            
            # 如果不区分大小写，则将文本转换为小写
            if not case_sensitive:
                text = text.lower()
            
            for item in self._sync_items:
                speech, video, keyword = item
                
                # 关键词可能包含多个，以逗号分隔
                keywords = keyword.split(',')
                
                for k in keywords:
                    k = k.strip()
                    if not k:
                        continue
                    
                    # 如果不区分大小写，则将关键词转换为小写
                    if not case_sensitive:
                        k = k.lower()
                    
                    # 根据匹配模式进行匹配
                    if match_mode == "contains" and k in text:
                        self.logger.info(f"匹配到关键词: {k}, 话术={speech}, 视频={video}")
                        return item
                    elif match_mode == "exact" and k == text:
                        self.logger.info(f"精确匹配到关键词: {k}, 话术={speech}, 视频={video}")
                        return item
                    elif match_mode == "regex":
                        try:
                            if re.search(k, text):
                                self.logger.info(f"正则匹配到关键词: {k}, 话术={speech}, 视频={video}")
                                return item
                        except re.error as e:
                            self.logger.error(f"正则表达式错误: {str(e)}, 关键词={k}")
            
            return None
    
    def save_sync_items(self, file_path: Optional[str] = None) -> bool:
        """
        保存音画同步项目到文件
        
        @param file_path: 文件路径，如果为 None 则使用默认路径
        @return: 是否成功保存
        """
        try:
            # 使用默认路径或指定路径
            save_path = file_path or self._config.get("default_save_path", "")
            if not save_path:
                self.logger.warning("保存路径为空，无法保存音画同步项目")
                return False
            
            # 确保目录存在
            save_dir = os.path.dirname(save_path)
            if not os.path.exists(save_dir):
                os.makedirs(save_dir)
            
            # 转换数据格式为JSON格式
            sync_data = []
            for speech, video, keyword in self._sync_items:
                # 获取视频文件名
                video_name = os.path.basename(video)
                # 将话术拆分为列表
                speech_list = [s.strip() for s in speech.split(',') if s.strip()]
                # 将关键词拆分为列表
                keyword_list = [k.strip() for k in keyword.split(',') if k.strip()]
                
                sync_data.append({
                    "video": video_name,
                    "text": speech_list,
                    "keywords": keyword_list
                })
            
            # 保存到文件
            with open(save_path, 'w', encoding='utf-8') as f:
                json.dump(sync_data, f, ensure_ascii=False, indent=2)
            
            self.logger.info(f"已保存 {len(sync_data)} 个音画同步项目到文件: {save_path}")
            return True
        
        except Exception as e:
            self.logger.error(f"保存音画同步项目失败: {str(e)}")
            import traceback
            self.logger.error(traceback.format_exc())
            return False
    
    def load_sync_items(self, file_path: Optional[str] = None) -> bool:
        """
        从文件加载音画同步项目
        
        @param file_path: 文件路径，如果为 None 则使用默认路径
        @return: 是否成功加载
        """
        try:
            # 使用默认路径或指定路径
            load_path = file_path or self._config.get("default_save_path", "")
            if not load_path:
                self.logger.warning("加载路径为空，无法加载音画同步项目")
                return False
            
            # 检查文件是否存在
            if not os.path.exists(load_path):
                self.logger.info(f"配置文件不存在，跳过加载: {load_path}")
                return False
            
            # 从文件加载数据
            with open(load_path, 'r', encoding='utf-8') as f:
                sync_data = json.load(f)
            
            # 清空现有项目
            with self._lock:
                self._sync_items.clear()
            
            # 获取配置文件所在目录
            config_dir = os.path.dirname(load_path)
            
            # 添加项目
            for item in sync_data:
                video_name = item.get("video", "")
                speech_list = item.get("text", [])
                keyword_list = item.get("keywords", [])
                
                # 构建完整的视频路径
                video_path = os.path.join(config_dir, video_name)
                
                # 将话术列表和关键词列表转换为字符串
                speech_text = ", ".join(speech_list)
                keyword_text = ", ".join(keyword_list)
                
                # 添加到项目列表
                self._sync_items.append((speech_text, video_path, keyword_text))
            
            self.logger.info(f"已从文件加载 {len(sync_data)} 个音画同步项目: {load_path}")
            return True
        
        except Exception as e:
            self.logger.error(f"加载音画同步项目失败: {str(e)}")
            import traceback
            self.logger.error(traceback.format_exc())
            return False
    
    def configure(self, config: Dict[str, Any]) -> None:
        """
        配置音画同步服务
        
        @param config: 配置信息
        """
        with self._lock:
            # 更新配置
            for key, value in config.items():
                self._config[key] = value
            
            self.logger.info(f"配置音画同步服务: {config}")
            
            # 如果配置中包含默认保存路径，则尝试加载文件
            if "default_save_path" in config:
                save_path = config["default_save_path"]
                if save_path:  # 只有在路径非空时才尝试加载
                    self.load_sync_items(save_path)
                else:
                    self.logger.warning("配置的默认保存路径为空，跳过加载")
    
    def start(self) -> bool:
        """
        启动服务
        
        @return: 是否成功启动
        """
        try:
            # 尝试加载音画同步项目，只有在有默认保存路径时才加载
            if self._config.get("default_save_path"):
                self.load_sync_items()
            else:
                self.logger.info("未设置默认保存路径，跳过加载音画同步项目")
            
            # 设置评论服务的监听器，用于匹配关键词
            comment_service = ServiceLocator.get(ICommentService)
            if comment_service:
                comment_service.add_listener("comment_processed", self._on_comment_processed)
                self.logger.info("已设置评论处理监听器")
            
            self.logger.info("音画同步服务已启动")
            return True
            
        except Exception as e:
            self.logger.error(f"启动音画同步服务失败: {str(e)}")
            return False
    
    def stop(self) -> bool:
        """
        停止服务
        
        @return: 是否成功停止
        """
        try:
            # 如果启用了自动保存，则保存到文件
            if self._config.get("auto_save", True):
                self.save_sync_items()
            
            # 移除评论服务的监听器
            comment_service = ServiceLocator.get(ICommentService)
            if comment_service:
                comment_service.remove_listener("comment_processed", self._on_comment_processed)
                self.logger.info("已移除评论处理监听器")
            
            self.logger.info("音画同步服务已停止")
            return True
            
        except Exception as e:
            self.logger.error(f"停止音画同步服务失败: {str(e)}")
            return False
    
    def _on_comment_processed(self, event: Any) -> None:
        """
        处理评论处理完成事件
        
        @param event: 事件对象
        """
        if not self._enabled:
            return
        
        try:
            # 获取评论内容
            comment = event.data
            if not comment or not hasattr(comment, "content"):
                return
            
            # 匹配关键词
            matched_item = self.match_keyword(comment.content)
            if matched_item:
                speech, video, keyword = matched_item
                
                # 触发关键词匹配事件
                self.notify("keyword_matched", {
                    "comment": comment,
                    "speech": speech,
                    "video": video,
                    "keyword": keyword
                })
                
                # 播放对应的话术和视频
                self._play_matched_item(speech, video)
                
        except Exception as e:
            self.logger.error(f"处理评论失败: {str(e)}")
    
    def _play_matched_item(self, speech: str, video: str) -> None:
        """
        播放匹配到的话术和视频
        
        @param speech: 话术
        @param video: 视频
        """
        try:
            # 获取语音服务
            speech_service = ServiceLocator.get(ISpeechService)
            if speech_service:
                # 合成并播放话术
                speech_service.synthesize_async(speech)
                self.logger.info(f"播放话术: {speech}")
            
            # 触发视频播放事件
            self.notify("play_video", {
                "video": video
            })
            
            self.logger.info(f"播放视频: {video}")
            
        except Exception as e:
            self.logger.error(f"播放匹配项目失败: {str(e)}") 